Skip to Content

为了方便理解,我们可以把整个过程想象成一个图书馆的座位管理系统。

  • 物理内存 (RAM):图书馆里所有可用的座位,数量有限。
  • 程序/进程:需要座位来读书学习的人。
  • 操作系统:图书馆的管理员,负责给来的人分配座位,并记录谁坐在哪里。

一、 为什么需要内存分配?

计算机的核心是 CPU,但 CPU 只直接与内存(RAM)打交道,它无法直接处理硬盘上的数据。因此,任何程序和它需要的数据都必须首先被加载到内存中才能运行。

内存分配要解决的核心问题是:如何在有限的物理内存中,安全、高效地为多个同时运行的进程分配空间。

这里的关键词是:

  • 有限:物理内存大小是固定的。
  • 多个进程:现代操作系统都是多任务的,需要同时支持很多进程。
  • 安全:不能让一个进程访问到另一个进程的内存空间,否则会导致数据错乱和系统崩溃(A 同学不能看 B 同学的书,更不能乱涂乱画)。
  • 高效:分配和回收内存的速度要快,并且要尽可能地利用好每一寸内存空间。

二、 核心基本概念

在深入策略之前,必须理解两个地址的概念:

  1. 物理地址 (Physical Address):内存芯片上实实在在的地址,就像图书馆里每个座位的唯一编号(例如,A区3排5号)。CPU 最终要靠这个地址来读取数据。
  2. 逻辑地址 (Logical Address) / 虚拟地址 (Virtual Address):程序自己视角里的地址。每个程序都以为自己独占了一大片从 0 开始的、连续的内存空间。这就像每个来读书的人都有一本自己的笔记本,页码从第1页开始,他只关心自己笔记本的页码,不关心自己坐在哪个座位上。

地址转换 (Address Translation):操作系统和硬件(MMU - 内存管理单元)协同工作,将程序使用的逻辑地址实时地“翻译”成物理地址。这个翻译过程是内存管理的核心。就像管理员通过座位登记表,把“张三笔记的第5页”这个逻辑位置,对应到“A区3排5号”这个物理座位上。


三、主要的内存分配策略

内存分配技术是逐步演进的,主要解决了“碎片化”和“空间不足”两大问题。

阶段一:连续分配 (Contiguous Allocation)

这是最早期、最简单的策略。当一个进程需要内存时,操作系统会为它寻找一块 足够大的、连续的 物理内存空间,然后把整个进程放进去。

  • 优点:简单,易于实现。一旦分配完成,程序内部的地址访问非常快,因为逻辑地址和物理地址只需一个简单的基址寄存器就能转换(物理地址 = 基址 + 逻辑地址)。
  • 缺点:会产生严重的 内存碎片 (Fragmentation) 问题。

内存碎片分为两种:

  1. 外部碎片 (External Fragmentation):在多个进程被装入、释放后,内存中会散布着许多不连续的小空闲块。这些小块的总和可能很大,但因为它们不连续,所以无法分配给任何一个需要大块连续内存的进程。

    • 图书馆比喻:图书馆里空着 20 个座位,但它们都分散在不同角落,而一个 5 人的小组想坐在一起却找不到位置。
  2. 内部碎片 (Internal Fragmentation):操作系统为了管理的方便,通常会按固定大小的块(例如 4KB)来分配内存。如果一个进程需要 10KB,系统可能会分给它 3 个块,也就是 12KB。那么多出来的 2KB 就浪费了,这块空间虽然分配给了该进程,但它并不会使用。

    • 图书馆比喻:图书馆的桌子都是 4 人桌。来了一个 3 人的小组,他们占了一张 4 人桌,剩下的 1 个座位就浪费了(内部碎片)。

为了在连续分配中找到合适的空闲块,有几种常见的算法:

  • 首次适应 (First-Fit):从头开始查找,找到第一个足够大的空闲块就分配。
    • 优点:快。
    • 缺点:容易在内存低地址部分留下很多小碎片。
  • 最佳适应 (Best-Fit):遍历所有空闲块,找到一个和需求大小最接近(但要大于等于)的空闲块。
    • 优点:能最大限度地保留大块空闲区。
    • 缺点:慢(要遍历所有),且容易产生大量极小的、几乎无法使用的外部碎片。
  • 最差适应 (Worst-Fit):遍历所有空闲块,找到最大的那个分配。
    • 优点:剩下的空闲块会比较大,不容易产生小碎片。
    • 缺点:大块内存被快速消耗,当需要大内存的进程到来时可能无法满足。

连续分配因为外部碎片问题,在现代通用操作系统中已基本被淘汰。

阶段二:非连续分配 (Non-Contiguous Allocation)

为了解决外部碎片问题,人们想到:为什么一个进程必须占用连续的内存呢?只要能把逻辑地址和物理地址正确地对应起来,进程可以被分散地存放在物理内存的各个角落。

这催生了两种关键技术:分页 (Paging)分段 (Segmentation)

1. 分页 (Paging)

这是现代操作系统最核心的内存管理技术。

  • 核心思想
    • 逻辑地址空间 划分为大小相等的块,称为 页 (Page)
    • 物理内存空间 划分为同样大小的块,称为 帧 (Frame) 或页框。
    • 当进程需要内存时,操作系统会为其分配若干个 物理帧,这些帧在物理上 可以不连续
    • 操作系统为每个进程维护一个 页表 (Page Table),记录该进程的每个逻辑页被存放到了哪个物理帧中。
  • 工作流程
    1. CPU 产生一个逻辑地址,这个地址包含两部分:页号 (Page Number)页内偏移量 (Offset)
    2. MMU(硬件)根据进程的页表基地址,查找页表。
    3. 页号 作为索引,在页表中找到对应的 帧号 (Frame Number)
    4. 将得到的 帧号页内偏移量 拼接起来,形成最终的 物理地址
    5. 访问该物理地址。
  • 优点
    • 彻底解决外部碎片问题:因为内存是以帧为单位分配的,任何一个空闲的帧都可以被利用起来。
    • 内存使用率高:分配和管理非常灵活。
  • 缺点
    • 存在内部碎片:一个进程的最后一页通常不会被完全用满,这会产生少量内部碎片。但因为页的大小通常比较小(如 4KB),这种浪费可以接受。
    • 页表开销:如果逻辑地址空间很大,页表本身也会占用相当大的内存空间。为了解决这个问题,又发展出了多级页表、反向页表等技术。
2. 分段 (Segmentation)
  • 核心思想:从程序员和逻辑结构的角度来划分内存。一个程序通常由多个逻辑部分组成,如主代码段(Code Segment)、数据段(Data Segment)、栈段(Stack Segment)等。分段就是按这些逻辑意义来划分内存。
  • 特点:每个段的大小 不固定。地址由 段号段内偏移量 组成。操作系统维护一个 段表 (Segment Table),记录每个段的基地址和长度。
  • 优点
    • 逻辑清晰:符合程序的逻辑结构,便于共享和保护(例如,代码段可以设为只读并被多个进程共享)。
  • 缺点
    • 会产生外部碎片:因为段的大小是可变的,当段被换入换出后,仍然会产生和连续分配类似的外部碎片问题。
3. 段页式 (Segmentation with Paging)

这是分段和分页的结合,Intel x86 架构就采用了这种模式。

  • 核心思想:先将程序按逻辑结构 分段,然后再将每个段 分页
  • 地址转换:逻辑地址 -> (段表) -> 段内地址 -> (页表) -> 物理地址。
  • 它结合了分段的逻辑清晰和分页的内存利用率高的优点,但管理和地址转换过程也更复杂。

四、终极形态:虚拟内存 (Virtual Memory)

基于 分页技术,操作系统实现了一个非常强大的功能——虚拟内存

  • 核心思想:程序运行时,没有必要 把它的所有部分都一次性加载到物理内存中。只需要加载当前正在使用的那部分即可。

  • 实现方式请求分页 (Demand Paging)

    1. 当一个进程启动时,操作系统只为其创建页表等数据结构,但并不立即为其加载任何页面到物理内存。
    2. 当 CPU 尝试访问一个逻辑地址,而 MMU 在页表中发现这个页 不在物理内存中 时(页表项中有个“有效/无效”位来标记),会触发一个硬件中断,称为 缺页中断 (Page Fault)
    3. 操作系统接管这个中断,执行以下操作: a. 检查该访问是否合法。 b. 在硬盘(交换空间/Swap Space)上找到该页的数据。 c. 在物理内存中寻找一个空闲的 帧 (Frame)。 d. 如果没有空闲帧,就需要执行 页面置换算法 (Page Replacement Algorithm),选择一个当前在内存中但不常用的页面,将其写回硬盘(如果它被修改过),以腾出空间。 e. 将需要的页面从硬盘加载到腾出的帧中。 f. 更新进程的页表,标记该页已在内存中,并记录对应的帧号。 g. 返回到原来的指令,重新执行。这次访问就能成功了。
  • 带来的巨大好处

    1. 更大的程序空间:程序认为自己拥有的内存(虚拟内存)可以远远大于实际的物理内存。
    2. 更高的并发度:因为每个进程实际只占用少量物理内存,所以物理内存可以容纳更多的进程同时运行。
    3. 更快的启动速度:进程启动时无需加载整个程序,只需加载必要部分,大大提高了响应速度。
  • 关键技术:页面置换算法 当发生缺页中断且没有空闲帧时,选择哪个页面“牺牲”掉,直接影响系统性能。常见算法有:

    • FIFO (先进先出):淘汰最早进入内存的页面。简单但性能不佳。
    • LRU (Least Recently Used - 最近最少使用):淘汰最长时间未被访问的页面。基于“局部性原理”(程序在一段时间内倾向于访问集中的内存区域),性能很好,但完美实现的硬件开销大。
    • Clock (时钟) / NRU (Not Recently Used):LRU 的一种高效近似实现,通过一个引用位来判断页面近期是否被访问过,是现实世界中常用的算法。

总结

策略核心思想优点缺点
连续分配整个进程放入一块连续内存简单,访问快严重的外部碎片
分页逻辑和物理空间都切成等大块,通过页表映射无外部碎片,内存利用率高内部碎片,页表开销
分段按程序逻辑结构划分,段大小可变逻辑清晰,易于共享保护外部碎片
虚拟内存 (请求分页)只在需要时才将页面调入内存突破物理内存限制,提高并发缺页中断有开销,需要页面置换算法

现代操作系统(如 Windows, Linux, macOS)的内存管理都是以 基于分页的虚拟内存 为基础的。它巧妙地结合了硬件(MMU)和软件(OS),为我们创造了一个既安全又看似无限的内存环境,是计算机科学中一个非常优雅和强大的解决方案。

Last updated on